package com.jb.faces.component;

import com.jb.web.renderer.RenderKitImpl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.jb.faces.context.FacesContext;
import com.jb.faces.event.AbortProcessingException;
import com.jb.faces.event.FacesEvent;
import com.jb.faces.event.FacesListener;
import com.jb.faces.render.Renderer;


public abstract class UIComponentBase implements UIComponent {
 
    protected String clientId;
    private Children children = new Children();
    private Facets facets = new Facets();
    private List eventQueue = new ArrayList();
    private List facesListeners = new ArrayList();
    private String id;
    private String rendererType;
    private UIComponent parent;
    boolean rendered;

    public List getChildren() {
        children.setParent(this);
        return children;
    }
    
    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientId(FacesContext pCtx) {
        return clientId;
    }

    public String getClientId() {
        return clientId;
    }

    public Object getFacet(String key) {
        return facets.get(key);
    }

    public Map getFacets() {
        facets.setParent(this);
        return facets;
    }

    public Iterator getFacetsAndChildren() {
        ArrayList list = new ArrayList();
        list.addAll(facets.values());
        list.addAll(children);

        return list.iterator();
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public UIComponent getParent() {
        return parent;
    }

    public void setRendered(boolean rendered) {
        this.rendered = rendered;
    }

    public boolean isRendered() {
        return rendered;
    }

    public void setRendererType(String rendererType) {
        this.rendererType = rendererType;
    }

    public String getRendererType() {
        return rendererType;
    }

    /**
    * Add a facet to the component.
    *
    * @param name name of the facet.
    * @param component component that will be added as facet.
    */
    public void addFacet(String name, UIComponent component) {
        getFacets().put(name, component);
    }

    public void broadcast(FacesEvent event) throws AbortProcessingException {
    }

    public void processApplication(FacesContext pCtx) {
        Iterator iter = getFacetsAndChildren();

        while (iter.hasNext()) {
            UIComponentBase uicom = (UIComponentBase) iter.next();
            uicom.processApplication(pCtx);

            if (pCtx.getRenderResponse()) {
                break;
            }
        }
    }

    public void processDecodes(FacesContext pCtx) {
        Iterator iter = getFacetsAndChildren();

        while (iter.hasNext()) {
            UIComponentBase uicom = (UIComponentBase) iter.next();
            uicom.processDecodes(pCtx);

            if (pCtx.getRenderResponse()) {
                break;
            }
        }
        Renderer renderer = RenderKitImpl.getRenderer(this);
        renderer.decode(pCtx, this);
  //      decode(pCtx);
    }

    public void processUpdates(FacesContext pCtx) {
        if (pCtx.getRenderResponse()) {
            return;
        }
        Iterator iter = getFacetsAndChildren();

        while (iter.hasNext()) {
            UIComponentBase uicom = (UIComponentBase) iter.next();
            uicom.processUpdates(pCtx);

            if (pCtx.getRenderResponse()) {
                break;
            }
        }
    }

    public void processValidators(FacesContext pCtx) {
        Iterator iter = getFacetsAndChildren();

        while (iter.hasNext()) {
            UIComponentBase uicom = (UIComponentBase) iter.next();
            uicom.processValidators(pCtx);

            if (pCtx.getRenderResponse()) {
                break;
            }
        }
    }

    public void queueEvent(FacesEvent e) {
        eventQueue.add(e);
    }

    protected FacesListener[] getFacesListeners(java.lang.Class clazz) {
        List facesListener = new ArrayList();

        for (int i = 0; i < facesListeners.size(); i++) {
            FacesListener listener = (FacesListener) facesListeners.get(i);

            if (clazz.isInstance(listener)) {
                facesListener.add(listener);
            }
        }

        return (FacesListener[]) facesListener.toArray();
    }

    protected void addFacesListener(FacesListener listener) {
        //
        //only add if it does not exist. We do not add it twice
        //
        if (facesListeners.indexOf(listener) == -1) {
            facesListeners.add(listener);
        }
    }

    protected void processEvent(FacesContext pCtx) {
        Iterator iter = eventQueue.iterator();

        while (iter.hasNext()) {
            try {
                broadcast((FacesEvent) iter.next());
            } catch (AbortProcessingException e) {
                //event handling error;
            }
        }

        eventQueue.clear();

        iter = getFacetsAndChildren();

        while (iter.hasNext()) {
            UIComponentBase uicom = (UIComponentBase) iter.next();
            uicom.processEvent(pCtx);
        }
    }

    protected void removeFacesListener(FacesListener listener) {
        facesListeners.remove(listener);
    }

    static class Facets extends HashMap {
        private UIComponentBase parent;

        public Object put(Object key, Object obj) {
            if (obj instanceof UIComponentBase) {
                ((UIComponentBase) obj).parent = parent;
            }

            return super.put(key, obj);
        }

        void setParent(UIComponentBase parent) {
            this.parent = parent;
        }
    }

    static class Children extends ArrayList {
        private UIComponentBase parent;

        public boolean add(Object obj) {
            if (obj instanceof UIComponentBase) {
                ((UIComponentBase) obj).parent = parent;
            }

            return super.add(obj);
        }

        void setParent(UIComponentBase parent) {
            this.parent = parent;
        }
    }
}
